home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Modules / BackSpaceModules / Source / SchoolView / SchoolView.m < prev    next >
Text File  |  1994-04-17  |  11KB  |  451 lines

  1. /**********************************************************************
  2.   SchoolView.m
  3.  
  4.   Author:    David C. "Slam" Lambert
  5.   Date:        1 February, 1993
  6. **********************************************************************/
  7. #import <math.h>
  8. #import <string.h>
  9. #import <memory.h>
  10. #import <appkit/Font.h>
  11. #import <appkit/View.h>
  12. #import <appkit/Button.h>
  13. #import <appkit/NXImage.h>
  14. #import <appkit/TextField.h>
  15. #import <appkit/NXColorWell.h>
  16. #import    <defaults/defaults.h>
  17. #import <dpsclient/wraps.h>
  18. #import "Thinker.h"
  19. #import "School.h"
  20. #import "SchoolView.h"
  21.  
  22. #ifndef FONT_SIZE
  23. #define FONT_SIZE    (20.0)
  24. #endif
  25.  
  26. static NXDefaultsVector    SchoolViewDefaults = 
  27.     {
  28.         { "SchoolCount", "40" },
  29.         { "SchoolDistExp", "3.0" },
  30.         { "SchoolMomentum", "0.05" },
  31.         { "SchoolMinRadius", "60.0" },
  32.         { "SchoolMaxVelocity", "18.0" },
  33.         { "SchoolMinVelocity", "0.0" },
  34.         { "SchoolAccLimit", "5.0" },
  35.         { "SchoolAvoidFact", "40" },
  36.         { "SchoolMatchFact", "0.7" },
  37.         { "SchoolCenterFact", "13" },
  38.         { "SchoolTargetFact", "10" },
  39.         { "SchoolFollowsMouse", "0" },
  40.         { "SchoolChangeGoalFreq", "20"},
  41.         { NULL },
  42.     };
  43.  
  44. static float
  45. uniform(long seed)
  46. {
  47.     long            k;
  48.     long            z;
  49.     static long     s1;
  50.     static long     s2;
  51.  
  52.     if (seed != 0)  {
  53.         s1 = seed;
  54.         s2 = ~seed;
  55.     }
  56.  
  57.     k = s1 / 53668;
  58.     s1 = 40014 * (s1 - k * 53668) - k * 12211;
  59.     if (s1 < 0)
  60.         s1 = s1 + 2147483563;
  61.  
  62.     k  = s2 / 52774;
  63.     s2 = 40692 * (s2 - k * 52774) - k * 3791;
  64.     if (s2 < 0)
  65.         s2 = s2 + 2147483399;
  66.  
  67.     z = s1 - s2;
  68.     if (z < 1)
  69.         z = z + 2147483562;
  70.  
  71.     return(((float)z * 4.656613e-10));
  72. }
  73.  
  74. @implementation SchoolView
  75.  
  76. + initialize
  77. {
  78.     NXRegisterDefaults([NXApp appName], SchoolViewDefaults);
  79.     return self;
  80. }
  81.  
  82. - getDefaults
  83. {
  84.     const    char    *theName = [NXApp appName];
  85.  
  86.     schoolCount = atoi(NXGetDefaultValue(theName, "SchoolCount"));
  87.     maxVel = atof(NXGetDefaultValue(theName, "SchoolMaxVelocity"));
  88.     minVel = atof(NXGetDefaultValue(theName, "SchoolMinVelocity"));
  89.     distExp = atof(NXGetDefaultValue(theName, "SchoolDistExp"));
  90.     momentum = atof(NXGetDefaultValue(theName, "SchoolMomentum"));
  91.     accLimit = atof(NXGetDefaultValue(theName, "SchoolAccLimit"));
  92.     avoidFact = atof(NXGetDefaultValue(theName, "SchoolAvoidFact"));
  93.     matchFact = atof(NXGetDefaultValue(theName, "SchoolMatchFact"));
  94.     minRadius = atof(NXGetDefaultValue(theName, "SchoolMinRadius"));
  95.     centerFact = atof(NXGetDefaultValue(theName, "SchoolCenterFact"));
  96.     targetFact = atof(NXGetDefaultValue(theName, "SchoolTargetFact"));
  97.     followMouse = (BOOL)atoi(NXGetDefaultValue(theName, "SchoolFollowsMouse"));
  98.     goalChgFreq = atoi(NXGetDefaultValue(theName, "SchoolChangeGoalFreq"));
  99.  
  100.     return self;
  101. }
  102.  
  103. - writeDefaults
  104. {
  105.     char    string[100];
  106.     const    char    *theName = [NXApp appName];
  107.  
  108.     sprintf(string, "%d", schoolCount);
  109.     NXWriteDefault(theName, "SchoolCount", string);
  110.     sprintf(string, "%.4f", maxVel);
  111.     NXWriteDefault(theName, "SchoolMaxVelocity", string);
  112.     sprintf(string, "%.4f", avoidFact);
  113.     NXWriteDefault(theName, "SchoolAvoidFact", string);
  114.     sprintf(string, "%d", followMouse);
  115.     NXWriteDefault(theName, "SchoolFollowsMouse", string);
  116.  
  117.     return self;
  118. }
  119.  
  120. - initFrame:(const NXRect *)frameRect
  121. {
  122.     [super initFrame:frameRect];
  123.     [self setOpaque:YES];
  124.     [self setClipping:NO];
  125.  
  126.     uniform(time(NULL));
  127.     theSchools = (BOID *)malloc(0);
  128.     oldString = malloc(0);
  129.     schoolString = malloc(0);
  130.     coords = (float *)malloc(0);
  131.     oldCoords = (float *)malloc(0);
  132.  
  133.     [self getDefaults];
  134.     [self allocateGState];
  135.     [self setValues];
  136.     [self setup];
  137.     PSWDavesDefFont("Fish");
  138.     PSselectfont("Fish", FONT_SIZE);
  139.  
  140.     return self;
  141. }
  142.  
  143. - free
  144. {
  145.     free(theSchools);
  146.     free(oldString);
  147.     free(schoolString);
  148.     free(coords);
  149.     free(oldCoords);
  150.  
  151.     [targetImage free];
  152.     return [super free];
  153. }
  154.  
  155. - setValues
  156. {
  157.     [countField setIntValue:schoolCount];
  158.     [countSlider setIntValue:schoolCount];
  159.     [vMaxField setIntValue:maxVel];
  160.     [vMaxSlider setIntValue:maxVel];
  161.     [avoidField setIntValue:avoidFact];
  162.     [avoidSlider setIntValue:avoidFact];
  163.     [followSwitch setState:followMouse];
  164.     return self;
  165. }
  166.  
  167. - awakeFromNib
  168. {
  169.     [self setValues];
  170.     targetImage = [NXImage findImageNamed:"Target"];
  171.     distComp = FONT_SIZE * 0.707;
  172.     return self;
  173. }
  174.  
  175. - takeValues:sender
  176. {
  177.     schoolCount = [countSlider floatValue];
  178.     maxVel = [vMaxSlider floatValue];
  179.     avoidFact = [avoidSlider floatValue];
  180.     followMouse = [followSwitch state];
  181.     targetFact = (followMouse) ? 200.0 : 10.0;
  182.  
  183.     [countField setIntValue:schoolCount];
  184.     [vMaxField setIntValue:maxVel];
  185.     [avoidField setIntValue:avoidFact];
  186.  
  187.     [self setup];
  188.     [self writeDefaults];
  189.  
  190.     return self;
  191. }
  192.  
  193. - setup
  194. {
  195.     int        i;
  196.     int        j;
  197.     BOID    *b;
  198.  
  199.     theSchools = realloc(theSchools, sizeof(BOID) * schoolCount);
  200.  
  201.     oldString = (char *)realloc(oldString, sizeof(char) * (schoolCount + 1));
  202.     schoolString = (char *)realloc(schoolString, sizeof(char) * (schoolCount + 1));
  203.     coords = (float *)realloc(coords, sizeof(float) * schoolCount * 2);
  204.     oldCoords = (float *)realloc(oldCoords, sizeof(float) * schoolCount * 2);
  205.  
  206.     bzero(coords, sizeof(float) * schoolCount * 2);
  207.     bzero(oldCoords, sizeof(float) * schoolCount * 2);
  208.  
  209.     memset(oldString, 'A', schoolCount);
  210.     memset(schoolString, '0', schoolCount);
  211.     oldString[schoolCount] = schoolString[schoolCount] = '\0';
  212.  
  213.     for(i = 0, j = -2, b = theSchools; i < schoolCount; i++, b++, j+=2)    {
  214.         BOID_X(b) = NX_X(&frame) + uniform(0) * NX_WIDTH(&frame);
  215.         BOID_Y(b) = NX_Y(&frame) + uniform(0) * NX_HEIGHT(&frame);
  216.         BOID_XVEL(b) = 2.0 * (uniform(0)-0.5) * maxVel;
  217.         BOID_YVEL(b) = 2.0 * (uniform(0)-0.5) * maxVel;
  218.         BOID_XACC(b) = BOID_YACC(b) = 0.0;
  219.         if (i > 0)    {
  220.             coords[j] = (BOID_X(b) - BOID_X(b-1));
  221.             coords[j+1] = (BOID_Y(b) - BOID_Y(b-1));
  222.         }
  223.     }
  224.     goalPoint.x = NX_MIDX(&frame); goalPoint.y = NX_MIDY(&frame);
  225.  
  226.     if ([[self window] canStoreColor])
  227.         hasColor = YES;
  228.  
  229.     [self display];
  230.     return self;
  231. }
  232.  
  233. - inspector:sender
  234. {
  235.     char buf[1024];
  236.  
  237.     if (!thePanel)    {
  238.         sprintf(buf, "%s/SchoolView.nib", [sender moduleDirectory:"School"]);
  239.         [NXApp loadNibFile:buf owner:self withNames:NO];
  240.     }
  241.     [self getDefaults];
  242.     return thePanel;
  243. }
  244.  
  245. - (BOOL)ignoreMouseMovement
  246. {
  247.     return followMouse;
  248. }
  249.  
  250. - computeAccelerations
  251. {
  252.     int        i;
  253.     int        j;
  254.     float    cAx;
  255.     float    cAy;
  256.     float    aVx;
  257.     float    aVy;
  258.     float    dist;
  259.     float    aMag;
  260.     float    xDiff;
  261.     float    yDiff;
  262.     float    adjDist;
  263.     float    adjDistSum;
  264.     BOID    *b0;
  265.     BOID    *b1;
  266.     NXPoint    tmpPoint;
  267.     static unsigned counter;
  268.  
  269.     adjDist = 0;
  270.     if (window != nil && followMouse)    {
  271.         [window getMouseLocation:&tmpPoint];
  272.         if (NXMouseInRect(&tmpPoint, &frame, NO))    {
  273.             goalPoint = tmpPoint;
  274.             goalPoint.x -= 10.0; goalPoint.y -= 16.0;
  275.         }
  276.     }
  277.     else if (!((++counter) % goalChgFreq))    {
  278.         goalPoint.x = NX_MIDX(&frame) + (uniform(0)-0.5) * 0.45 * NX_WIDTH(&frame);
  279.         goalPoint.y = NX_MIDY(&frame) + (uniform(0)-0.5) * 0.45 * NX_HEIGHT(&frame);
  280.     }
  281.     /* other school avoidance */
  282.     for(i = 0, b0 = theSchools; i < schoolCount; i++, b0++)    {
  283.         adjDistSum = 0.0;
  284.         cAx = cAy = aVx = aVy = 0.0;
  285.         BOID_XACC(b0) = BOID_YACC(b0) = 0.0;
  286.         for(j = 0, b1 = theSchools; j < schoolCount; j++, b1++)    {
  287.             if (b1 == b0) continue;
  288.             xDiff = XDIFF(b0, b1);
  289.             yDiff = YDIFF(b0, b1);
  290.             if (xDiff > NX_MIDX(&frame))
  291.                 xDiff = NX_MAXX(&frame) - xDiff;
  292.             if (yDiff > NX_MIDY(&frame))
  293.                 yDiff = NX_MAXY(&frame) - yDiff;
  294.             dist = NORM(xDiff, yDiff) - distComp;
  295.             if (dist > minRadius) continue;
  296.             else if (dist <= 0.0) dist = MIN_DIST;
  297.             adjDist = dist * dist;
  298.             adjDistSum += (1.0 / adjDist);
  299.             xDiff /= adjDist; yDiff /= adjDist;
  300.             cAx -= xDiff; cAy -= yDiff;
  301.             BOID_XACC(b0) += xDiff;    BOID_YACC(b0) += yDiff;
  302.             aVx += (BOID_XVEL(b1) / adjDist); aVy += (BOID_YVEL(b1) / adjDist);
  303.         }
  304.         xDiff = goalPoint.x - BOID_X(b0); yDiff = goalPoint.y - BOID_Y(b0);
  305.         BOID_XACC(b0) *= avoidFact;    BOID_YACC(b0) *= avoidFact;
  306.         aMag = NORM(BOID_XACC(b0), BOID_YACC(b0));
  307.         /* velocity matching */
  308.         if (adjDistSum != 0.0 && aMag < accLimit)    {
  309.             aVx /= adjDistSum; aVy /= adjDistSum;
  310.             BOID_XACC(b0) += ((aVx - BOID_XVEL(b0)) * matchFact);
  311.             BOID_YACC(b0) += ((aVy - BOID_YVEL(b0)) * matchFact);
  312.             aMag = NORM(BOID_XACC(b0), BOID_YACC(b0));
  313.             /* flock centering */
  314.             if (aMag < accLimit)    {
  315.                 BOID_XACC(b0) += cAx * centerFact;
  316.                 BOID_YACC(b0) += cAy * centerFact;
  317.                 aMag = NORM(BOID_XACC(b0), BOID_YACC(b0));
  318.                 /* target attraction */
  319.                 if (aMag < accLimit)    {
  320.                     BOID_XACC(b0) += xDiff * targetFact / adjDist;
  321.                     BOID_YACC(b0) += yDiff * targetFact / adjDist;
  322.                 }
  323.  
  324.             }
  325.         }
  326.         BOID_XACC(b0) += (uniform(0)-0.5);
  327.         BOID_YACC(b0) += (uniform(0)-0.5);
  328.         aMag = NORM(BOID_XACC(b0), BOID_YACC(b0));
  329.         if (aMag > accLimit)    {
  330.             BOID_XACC(b0) *= sqrt(accLimit/aMag);
  331.             BOID_YACC(b0) *= sqrt(accLimit/aMag);
  332.         }
  333.     }
  334.         
  335.     return self;
  336. }
  337.  
  338. - (BOOL)useBufferedWindow
  339. {
  340.     return NO;
  341. }
  342.  
  343. - didLockFocus
  344. {
  345.     PSselectfont("Fish", FONT_SIZE);
  346.     return self;
  347. }
  348.  
  349. - oneStep
  350. {
  351.     int        i;
  352.     int        j;
  353.     int        index;
  354.     float    avgIndex;
  355.     BOID    *b;
  356.     float    vMag;
  357.     float    oldX0 = BOID_X(theSchools);
  358.     float    oldY0 = BOID_Y(theSchools);
  359.     NXColor    theColor;
  360.     static float    hue;
  361.  
  362.     bcopy(coords, oldCoords, sizeof(float)*schoolCount*2);
  363.     bcopy(schoolString, oldString, sizeof(char)*schoolCount);
  364.  
  365.     PSnewinstance();
  366.     PSsetinstance(YES);
  367.     if (followMouse)
  368.         [targetImage composite:NX_COPY toPoint:&goalPoint];
  369.     PSsetinstance(NO);
  370.  
  371.     avgIndex = 0;
  372.     for(i = 0, j = -2, b = theSchools; i < schoolCount; i++, b++, j+=2)    {
  373.         /* apply accelerations */
  374.         BOID_XVEL(b) = BOID_XACC(b) + (1.0 + momentum) * BOID_XVEL(b);
  375.         BOID_YVEL(b) = BOID_YACC(b) + (1.0 + momentum) * BOID_YVEL(b);
  376.         vMag = 1.0e-6 + NORM(BOID_XVEL(b), BOID_YVEL(b));
  377.         if (vMag > maxVel)    {
  378.             BOID_XVEL(b) *= (maxVel/vMag);
  379.             BOID_YVEL(b) *= (maxVel/vMag);
  380.         }
  381.         else if (vMag < minVel)    {
  382.             BOID_XVEL(b) *= (minVel/vMag);
  383.             BOID_XVEL(b) *= (minVel/vMag);
  384.         }
  385.  
  386.         /* apply movements */
  387.         BOID_X(b) += BOID_XVEL(b);
  388.         BOID_X(b) = (BOID_X(b) > NX_MAXX(&frame)) ? 0.0 : BOID_X(b);
  389.         BOID_X(b) = (BOID_X(b) < NX_X(&frame)) ? NX_MAXX(&frame) : BOID_X(b);
  390.  
  391.         BOID_Y(b) += BOID_YVEL(b);
  392.         BOID_Y(b) = (BOID_Y(b) > NX_MAXY(&frame)) ? 0.0 : BOID_Y(b);
  393.         BOID_Y(b) = (BOID_Y(b) < NX_Y(&frame)) ? NX_MAXY(&frame) : BOID_Y(b);
  394.  
  395.         if (i > 0)    {
  396.             coords[j] = (BOID_X(b) - BOID_X(b-1));
  397.             coords[j+1] = (BOID_Y(b) - BOID_Y(b-1));
  398.         }
  399.         if (fabs(fabs(BOID_XVEL(b))-fabs(BOID_YVEL(b))) < maxVel/2.0)    {
  400.             if (BOID_XVEL(b) < 0.0)
  401.                 index = (BOID_YVEL(b) > 0.0) ? '1' : '2';
  402.             else
  403.                 index = (BOID_YVEL(b) > 0.0) ? '4' : '3';
  404.         }
  405.         else if (fabs(BOID_XVEL(b)) >  fabs(BOID_YVEL(b)))
  406.             index = (BOID_XVEL(b) < 0.0) ? 'l' : 'r';
  407.         else
  408.             index = (BOID_YVEL(b) > 0.0) ? 'u' : 'd';
  409.         schoolString[i] = index;
  410.     }
  411.     NXSetColor(NX_COLORBLACK);
  412.     PSWDavesXYShow(oldX0, oldY0,
  413.               oldString, oldCoords, schoolCount*2);
  414.  
  415.     if (hasColor)    {
  416.         theColor = NXConvertHSBToColor(hue/360.0, 1.0, 1.0);
  417.         hue = (hue < 360.0) ? hue+1.0 : 0.0;
  418.         NXSetColor(theColor);
  419.     }
  420.     else
  421.         NXSetColor(NX_COLORWHITE);
  422.  
  423.     PSWDavesXYShow(BOID_X(theSchools), BOID_Y(theSchools),
  424.               schoolString, coords, schoolCount*2);
  425.     [self computeAccelerations];
  426.     return self;
  427. }
  428.  
  429. - (const char *)windowTitle
  430. {
  431.     return "School        by David Lambert";
  432. }
  433.  
  434. - sizeTo:(NXCoord)width :(NXCoord)height
  435. {
  436.     [super sizeTo:width :height];
  437.     [self setup];
  438.     return self;
  439. }
  440.  
  441. - drawSelf:(const NXRect *)rects :(int)rectCount
  442. {
  443.     if (!rects || !rectCount) return self;
  444.     PSselectfont("Fish", FONT_SIZE);
  445.     NXSetColor(NX_COLORBLACK);
  446.     NXRectFill(rects);
  447.     return self;
  448. }
  449.  
  450. @end
  451.